package store

import (
	"context"
	"encoding/json"
	baseLog "gitee.com/zaiqiang231/go-base-app/base_app/log"
	"gitee.com/zaiqiang231/lovesport-authz-service/app/rpc"
	"gitee.com/zaiqiang231/lovesport-user-service/app/rpc/proto"
	"github.com/dgraph-io/ristretto"
	"github.com/ory/ladon"
	"github.com/pkg/errors"
	"sync"
)

var (
	cache     *Cache
	onceCache sync.Once
)

type Cache struct {
	lock     *sync.RWMutex
	Policies *ristretto.Cache
}

func InitCache() (*Cache, error) {
	onceCache.Do(func() {
		cacheConfig := &ristretto.Config{
			NumCounters: 1e7,     // number of keys to track frequency of (10M).
			MaxCost:     1 << 27, // maximum cost of cache (1GB 1<<30).
			BufferItems: 64,      // number of keys per Get buffer.
		}
		policyCache, err := ristretto.NewCache(cacheConfig)
		if err != nil {
			baseLog.Errorf("policyCache create failed, ", err)
			return
		}
		cache = &Cache{
			lock:     new(sync.RWMutex),
			Policies: policyCache,
		}
	})
	return cache, nil
}

func GetCache() *Cache {
	if cache == nil {
		baseLog.Errorf("cache instance is %v", cache)
	}
	return cache
}

func (c *Cache) Reload() error {
	c.lock.Lock()
	defer c.lock.Unlock()

	page := int64(1)
	pageSize := int64(-1)

	//policy
	policies := make(map[string][]*ladon.DefaultPolicy)
	policiesRsp, err := rpc.GetUserRpc().UserPoliciesRpcServiceClient.ListPolicies(context.Background(), &proto.ListPoliciesRequest{Page: &page, PageSize: &pageSize})
	if err != nil {
		return errors.Wrap(err, "list policies failed")
	}
	for _, v := range policiesRsp.Items {
		var policy ladon.DefaultPolicy
		if err := json.Unmarshal([]byte(v.PolicyShadow), &policy); err != nil {
			baseLog.Warnf("failed to load policy for %s, error: %s", v.Name, err.Error())
			continue
		}
		policies[v.UserName] = append(policies[v.UserName], &policy)
	}
	c.Policies.Clear()
	for key, val := range policies {
		c.Policies.Set(key, val, 1)
	}

	return nil
}

func (c *Cache) GetPolicy(key string) ([]*ladon.DefaultPolicy, error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	value, ok := c.Policies.Get(key)
	if !ok {
		return nil, errors.New("policy not found")
	}

	return value.([]*ladon.DefaultPolicy), nil
}
